package com.cloud.tsp.service.impl;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import cn.hutool.poi.excel.StyleSet;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.cloud.core.exception.MyException;
import com.cloud.api.domain.forward.*;
import com.cloud.api.domain.tsp.VehicleSale;
import com.cloud.api.domain.tsp.VehicleSaleExcel;
import com.cloud.api.mq.VehicleMsg;
import com.cloud.api.utils.ExcelUtils;
import com.cloud.mybatis.domain.TableDataInfo;
import com.cloud.tsp.mapper.VehiclePlatformMapper;
import com.cloud.tsp.service.IPlatformService;
import com.cloud.tsp.service.IVehiclePlatformService;
import com.cloud.tsp.service.IVehicleSaleService;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddressList;
import org.apache.poi.xssf.usermodel.XSSFCell;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.stream.function.StreamBridge;
import org.springframework.context.MessageSource;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import jakarta.annotation.Resource;
import jakarta.servlet.ServletOutputStream;
import jakarta.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.text.MessageFormat;
import java.util.*;
import java.util.stream.Collectors;

import static com.cloud.tsp.service.impl.VehicleInformationServiceImpl.readExcelHeaders;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author haohaibo
 * @since 2024-01-09
 */
@Service("vehiclePlatformService")
public class VehiclePlatformServiceImpl extends ServiceImpl<VehiclePlatformMapper, VehiclePlatform> implements IVehiclePlatformService {

    @Resource
    private VehiclePlatformMapper vehiclePlatformMapper;
    @Resource
    private IPlatformService platformService;
    @Resource
    private IVehicleSaleService vehicleSaleService;
    @Autowired
    private StreamBridge streamBridge;
    @Resource
    private MessageSource messageSource;
    @Resource(name = "myRedis")
    private RedisTemplate<String,Object> redisTemplate;

    @Override
    public TableDataInfo<VehiclePlatform> selectVehiclePlatformList(QueryVehiclePlat para) {
        Page<VehiclePlatform> page = new Page<>();
        page.setCurrent(para.getPage());
        page.setSize(para.getPageSize());
        IPage<VehiclePlatform> data = vehiclePlatformMapper.selectVehiclePlatformList(page, para);
        for (VehiclePlatform vehiclePlatform : data.getRecords()){
            vehiclePlatform.setVin(StrUtil.hide(vehiclePlatform.getVin(), 8, 11));
        }
        return TableDataInfo.build(data);
    }

    @Override
    public List<String> InsertVehiclePlatform(ForwardRequest para) {
        if (para.getVinList().size() == 0){
            return null;
        }
        Platform platform = platformService.getById(para.getPlatformId());
        List<VehiclePlatform> list = new ArrayList<>();
        List<VehicleMsg> vehicleMsgs = new ArrayList<>();
        /*LambdaQueryWrapper<VehicleSale> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.in(VehicleSale::getVin,para.getVinList())
                .select(VehicleSale::getVin,VehicleSale::getModelType);
        List<VehicleSale> VehicleList = vehicleSaleService.getBaseMapper().selectList(queryWrapper);*/
        List<VehicleSale> VehicleList = vehicleSaleService.getBaseMapper().selectBatchIds(para.getVinList());
//        Map<String, String> collect = VehicleList.stream().collect(Collectors.toMap(VehicleSale::getVin, VehicleSale::getModelType));
        for (VehicleSale vehicleSale : VehicleList){
            VehicleMsg vehicleMsg = new VehicleMsg();
            VehiclePlatform vehiclePlatform = new VehiclePlatform();
            vehiclePlatform.setPlatformId(para.getPlatformId());
            vehiclePlatform.setVin(vehicleSale.getVin());
            vehiclePlatform.setModel(vehicleSale.getModelType());
            vehiclePlatform.setStatus("0");
            list.add(vehiclePlatform);
            BeanUtils.copyProperties(vehiclePlatform,vehicleMsg);
            vehicleMsg.setPlatformName(platform.getPlatformName());
            vehicleMsgs.add(vehicleMsg);
        }
        this.saveBatch(list);
        mqNotice(vehicleMsgs);
        return list.stream().map(VehiclePlatform::getId).collect(Collectors.toList());
    }

    public void mqNotice(List<VehicleMsg> para){
        streamBridge.send("tspvehicle-out-0", MessageBuilder.withPayload(para).build());
    }

    @Override
    public void updateStatus(ForwardRequest para) {
        Platform platform = platformService.getById(para.getPlatformId());
        if ("1".equals(para.getStatus())){
            if (platform.getConnectionStatus() == 0){
                throw new MyException("wds.exception.forwardStart");
            }
        }
        List<VehiclePlatform> vehicleList = vehiclePlatformMapper.selectBatchIds(para.getVinList());
        vehicleList.stream().filter(vehicle ->para.getStatus().equals(vehicle.getStatus())).findFirst().ifPresent(vehicle -> {
            String msg = "wds.exception.forwardOff";
            if ("1".equals(para.getStatus())){
                msg = "wds.exception.forwardOn";
            }
            throw new MyException(msg);
        });
        List<VehiclePlatform> list = new ArrayList<>();
        List<String> vinList = new ArrayList<>();
        Map<String, String> map = new HashMap<>();
        List<VehicleMsg> vehicleMsgs = new ArrayList<>();
        for (String id : para.getVinList()){
            VehiclePlatform vehiclePlatform = new VehiclePlatform();
            vehiclePlatform.setId(id);
            vehiclePlatform.setStatus(para.getStatus());
            list.add(vehiclePlatform);
        }
        for (VehiclePlatform vehiclePlatform : vehicleList){
            VehicleMsg vehicleMsg = new VehicleMsg();
            vehicleMsg.setPlatformId(para.getPlatformId());
            vehicleMsg.setPlatformName(platform.getPlatformName());
            vehicleMsg.setVin(vehiclePlatform.getVin());
            vehicleMsg.setStatus(para.getStatus());
            vehicleMsgs.add(vehicleMsg);
            vinList.add(vehiclePlatform.getVin());
            map.put(vehiclePlatform.getVin(),para.getStatus());
        }
        this.updateBatchById(list);
        mqNotice(vehicleMsgs);
        if ("0".equals(para.getStatus())){
            redisTemplate.opsForHash().delete("ClientForward:" + para.getPlatformId(), vinList.toArray(new String[0]));
        }else {
            redisTemplate.opsForHash().putAll("ClientForward:" + para.getPlatformId(), map);
        }
    }

    @Override
    public void deletedById(String id) {
        String[] split = id.split(",");
        List<String> list = Arrays.asList(split);

        List<VehiclePlatform> vehiclePlatforms = vehiclePlatformMapper.selectBatchIds(list);

        String platformId = vehiclePlatforms.get(0).getPlatformId();
        Platform platform = platformService.getById(platformId);

        List<VehicleMsg> vehicleMsgs = new ArrayList<>();
        List<String> vinList = new ArrayList<>();
        for (VehiclePlatform vehiclePlatform : vehiclePlatforms){
            VehicleMsg vehicleMsg = new VehicleMsg();
            vehicleMsg.setStatus("0");
            vehicleMsg.setPlatformId(platformId);
            vehicleMsg.setPlatformName(platform.getPlatformName());
            vehicleMsg.setVin(vehiclePlatform.getVin());
            vehicleMsgs.add(vehicleMsg);
            vinList.add(vehiclePlatform.getVin());
        }

        this.removeBatchByIds(list);
        mqNotice(vehicleMsgs);
        redisTemplate.opsForHash().delete("ClientForward:" + platformId, vinList.toArray(new String[0]));
    }

    @Override
    public void downloadTemplate(HttpServletResponse response) throws IOException {
        Locale locale = LocaleContextHolder.getLocale();
        String fileName = "table.vinPlatRelate";
        String vin = "table.field.*vin";
        fileName = messageSource.getMessage(fileName, null, fileName, locale);
        vin = messageSource.getMessage(vin, null, vin, locale);
        ExcelWriter excelWriter = ExcelUtil.getWriter(fileName+".xlsx");
        excelWriter.setColumnWidth(0, 20);
        StyleSet styleSet = excelWriter.getStyleSet();

        styleSet.setBackgroundColor(IndexedColors.GREY_25_PERCENT,false);
        excelWriter.writeRow(Collections.singletonList(vin));
        response.setContentType("application/vnd.ms-excel;charset=utf-8");
        response.setHeader("Content-Disposition", "attachment;filename*=" + URLEncoder.encode(fileName+".xlsx", "UTF-8"));
        try {
            Workbook workbook = excelWriter.getWorkbook();
            CellStyle cellStyle = workbook.createCellStyle();
            Sheet sheet = workbook.getSheetAt(0);
            Row row = sheet.getRow(0);
            Cell cell = row.getCell(0);
            String r = ((XSSFCell) cell).getCTCell().getR();
            r = r.substring(0, 1);
            // 设置数据验证
            DataValidationHelper helper = sheet.getDataValidationHelper();
            CellRangeAddressList addressList = new CellRangeAddressList(1, 600, 0, 0);
            //vin唯一
            DataValidationConstraint constraint = helper.createCustomConstraint(MessageFormat.format("COUNTIF({0}:{0},{0}2)=1",r));
            DataValidation dataValidation = helper.createValidation(constraint, addressList);
            dataValidation.createErrorBox("错误：", "vin不允许重复");
            dataValidation.setShowErrorBox(true);
            dataValidation.setEmptyCellAllowed(true);
            dataValidation.setSuppressDropDownArrow(true);
            dataValidation.setShowPromptBox(true);
            dataValidation.setErrorStyle(DataValidation.ErrorStyle.STOP);

            sheet.addValidationData(dataValidation);

            //设置列为文本格式
            DataFormat format = workbook.createDataFormat();
            cellStyle.setDataFormat(format.getFormat("@"));
            excelWriter.setColumnStyle(0,cellStyle);
            ServletOutputStream out=response.getOutputStream();
            workbook.write(out);
            workbook.close();
            out.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public ResponseData importVinPlatRelate(String id, MultipartFile file, HttpServletResponse response, String key) throws IOException {
        List<ForwardExcel> list = ExcelUtils.read(file, ForwardExcel.class);

        //检查表头
        List<String> excelHeaders = readExcelHeaders(file);
        List<String> header = ExcelUtils.getExcelImportFieldsName(ForwardExcel.class);
        if (!header.equals(excelHeaders)) {
            throw new MyException("wds.exception.importHeaderError");
        }

        if (list.size() > 500){
            throw new MyException("wds.exception.importData");
        }
        if (list.size() == 0){
            throw new MyException("wds.exception.importNull");
        }

        //失败表，需要导出的数据
        List<ForwardExcel> fail = new ArrayList<>();
        //需要保存的数据
        List<VehiclePlatform> saveList = new ArrayList<>();
        Set<String> collect = list.stream().map(ForwardExcel::getVin).collect(Collectors.toSet());
        //返回前端的一键转发数据
        List<String> forwardList = new ArrayList<>();

        //校验是否已经存在转发表中，存在时放入错误表中，不保存，放入一键转发数据中
        checkRelateTable(id,collect,fail,saveList,forwardList);

        //校验是否在销售数据中，不存在拿到错误表中，不保存
        checkSale(collect,fail,saveList,forwardList);

        //发送消息队列
        Platform platform = platformService.getById(id);
        List<VehicleMsg> vehicleMsgs = new ArrayList<>();
        for (VehiclePlatform v : saveList){
            VehicleMsg vehicleMsg =  new VehicleMsg();
            vehicleMsg.setVin(v.getVin());
            vehicleMsg.setPlatformId(id);
            vehicleMsg.setPlatformName(platform.getPlatformName());
            vehicleMsg.setStatus("0");
            vehicleMsgs.add(vehicleMsg);
            forwardList.add(v.getId());
        }
        this.saveBatch(saveList);
        mqNotice(vehicleMsgs);
        String fileName = null;
        if (fail.size() > 0){
            fileName = exportErrList(fail, response);
        }
        ResponseData responseData = new ResponseData();
        responseData.setList(forwardList).setFileName(fileName);
        return responseData;
    }

    public void checkRelateTable(String id,Set<String> collect,List<ForwardExcel> fail,List<VehiclePlatform> saveList,List<String> forwardList){
        LambdaQueryWrapper<VehiclePlatform> queryRelate = new LambdaQueryWrapper<>();
        queryRelate.eq(VehiclePlatform::getPlatformId,id);
        queryRelate.in(VehiclePlatform::getVin,collect);
        List<VehiclePlatform> repeatList = vehiclePlatformMapper.selectList(queryRelate);
        Map<String, String> repeat = repeatList.stream().collect(Collectors.toMap(VehiclePlatform::getVin, VehiclePlatform::getId));

        for (String vin : collect){
            if (repeat.containsKey(vin)){
                ForwardExcel forwardExcel = new ForwardExcel();
                forwardExcel.setVin(vin);
                forwardExcel.setErrResult("该vin已经在转发表中存在");
                fail.add(forwardExcel);
                continue;
            }
            VehiclePlatform vehiclePlatform = new VehiclePlatform();
            vehiclePlatform.setId(IdUtil.getSnowflakeNextIdStr());
            vehiclePlatform.setVin(vin);
            vehiclePlatform.setPlatformId(id);
            vehiclePlatform.setStatus("0");
            saveList.add(vehiclePlatform);
        }
        //移除collect中重复表中数据
        repeat.keySet().forEach(collect::remove);
        forwardList.addAll(repeat.values());
    }

    public void checkSale(Set<String> collect,List<ForwardExcel> fail,List<VehiclePlatform> saveList,List<String> forwardList){
        if (collect.size() == 0){
            return;
        }
        LambdaQueryWrapper<VehicleSale> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.in(VehicleSale::getVin,collect);
        List<VehicleSale> saleList = vehicleSaleService.list(queryWrapper);
        List<String> existSale = saleList.stream().map(VehicleSale::getVin).collect(Collectors.toList());
        List<String> notExist = new ArrayList<>();
        for (String vin : collect){
            if (!existSale.contains(vin)){
                ForwardExcel forwardExcel = new ForwardExcel();
                forwardExcel.setVin(vin);
                forwardExcel.setErrResult("销售表中不存在该数据，请检查该vin是否有误");
                fail.add(forwardExcel);
                forwardList.remove(vin);
                notExist.add(vin);
            }
        }
        saveList.removeIf(vehiclePlatform -> notExist.contains(vehiclePlatform.getVin()));
    }

    @Override
    public void downloadFile(String fileName, HttpServletResponse response) {
        OutputStream os;
        String basePath = "/data/cglx/errFiles/";
        try {
            if(StrUtil.isNotEmpty(fileName)){
                response.reset();
                response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
                response.setContentType("application/octet-stream");
                byte[] bytes = FileUtil.readBytes(basePath + fileName);//通过文件的路径读取文件字节流
                os = response.getOutputStream();//通过response的输出流返回文件
                os.write(bytes);
                os.flush();
                os.close();
            }
        }catch (Exception e){
            System.out.println("文件下载失败");
        }
    }

    public String exportErrList(List<ForwardExcel> fail,HttpServletResponse response)throws IOException{
        Locale locale = LocaleContextHolder.getLocale();
        String basePath = "/data/cglx/errFiles/";
        String fileName = UUID.randomUUID().toString()+".xlsx";
//        fileName = messageSource.getMessage(fileName, null, fileName, locale);
        ExcelUtils.export(new File(basePath+fileName),fail,ForwardExcel.class);
        return fileName;
    }
}